package com.base.custom

import android.animation.ValueAnimator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import com.base.R

/**
 * 水波纹加载效果实现
 */
open class WaveView(context: Context,attrs: AttributeSet?,defStyleAttr: Int): View(context, attrs,defStyleAttr){
    companion object{
        const val Horizontal = 0// 横向
        const val Vertical = 1// 纵向
    }
    private val viewSize = PointF()
    private val wavePaint by lazy { val p = Paint(); p.isAntiAlias = true;  p } // 水波纹画笔
    private val backPaint by lazy { val p = Paint(); p.isAntiAlias = true;  p } // 背景画笔
    private val strokePaint by lazy { val p = Paint(); p.isAntiAlias = true; p.style = Paint.Style.STROKE; p } // 边框画笔
    private val backRect = RectF() // 背景绘制范围

    private var offset = 0f // 波纹扰动的偏移量
    private var waveWidth = 0f // 波纹宽度
    private var waveHeight = 0f// 波纹高度

    private val backgroundPath = Path()// 背景绘制路径
    private val strokePath = Path()// 边框路径
    private val wavePath  = Path()// 水波纹路径
    private val clip = Path()
    private val clipPath = Path()// 水波纹裁剪路径
    get() {
        field.reset()
        field.addPath(clip)
        field.op(wavePath,Path.Op.INTERSECT)// 裁剪掉矩形范围外部分
        return field
    }

    private var waveDirection = Horizontal//横向

    private var waveEnable = true//是否启用水波纹

    private val radiusArray = floatArrayOf(0f,0f,0f,0f,0f,0f,0f,0f)//圆角半径数组 8位 依次为topLeft,topLeft,topRight,topRight,bottomRight,bottomRight,bottomLeft,bottomLeft

    private var strokeSize = 0f // 边框线条宽度
    private var strokeColor = Color.TRANSPARENT // 边框线条颜色
    private var waveColor = Color.parseColor("#aa1B69F1")//水波纹颜色
    private var backColor = Color.WHITE // 背景颜色 默认白色

    private val amplitude = 0.11f // 波纹振幅与半径比
    private var progress = 0f // 当前进度
    private var maxProgress = 100f// 最大进度

    private val waveAnimator by lazy {// 水波纹不断扰动的动画
        val anim = ValueAnimator.ofFloat();
        anim.duration = 2000
        anim.repeatCount = ValueAnimator.INFINITE
        anim.interpolator = LinearInterpolator()
        anim.addUpdateListener { animation ->
            offset = animation.animatedValue as Float
            postInvalidate()
        }
        anim
    }

    constructor(context: Context): this(context,null)

    constructor(context: Context,attrs: AttributeSet?): this(context,attrs,0)

    init {
        val array: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView, defStyleAttr, 0);
        waveDirection = array.getInteger(R.styleable.WaveView_waveDirection,Horizontal)
        waveEnable = array.getBoolean(R.styleable.WaveView_waveEnable,waveEnable)
        strokeSize = (array.getDimensionPixelOffset(R.styleable.WaveView_waveStrokeSize,0)).toFloat()
        strokeColor = array.getColor(R.styleable.WaveView_waveStrokeColor,strokeColor)
        backColor = array.getColor(R.styleable.WaveView_waveBackground,backColor)
        waveColor = array.getColor(R.styleable.WaveView_waveColor,waveColor)
        progress = array.getFloat(R.styleable.WaveView_waveProgress,progress)
        maxProgress = array.getFloat(R.styleable.WaveView_waveMax,maxProgress)

        // 左右上下角圆弧半径
        val radius = array.getDimensionPixelOffset(R.styleable.WaveView_waveRadius,0)
        val topLeftRadius = (array.getDimensionPixelOffset(R.styleable.WaveView_waveTL,radius)).toFloat()
        val topRightRadius = (array.getDimensionPixelOffset(R.styleable.WaveView_waveTR,radius)).toFloat()
        val bottomLeftRadius = (array.getDimensionPixelOffset(R.styleable.WaveView_waveBL,radius)).toFloat()
        val bottomRightRadius = (array.getDimensionPixelOffset(R.styleable.WaveView_waveBR,radius)).toFloat()
        array.recycle()
        radiusArray[0] = topLeftRadius
        radiusArray[1] = topLeftRadius
        radiusArray[2] = topRightRadius
        radiusArray[3] = topRightRadius
        radiusArray[4] = bottomRightRadius
        radiusArray[5] = bottomRightRadius
        radiusArray[6] = bottomLeftRadius
        radiusArray[7] = bottomLeftRadius

        if(progress > maxProgress) progress = maxProgress

        strokePaint.color = strokeColor
        backPaint.color = backColor
        wavePaint.color = waveColor
        strokePaint.strokeWidth = strokeSize
        strokePaint.strokeJoin = Paint.Join.ROUND
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        viewSize.set(w.toFloat(),h.toFloat())
        sizeChanged(w.toFloat(),h.toFloat())
    }

    private fun sizeChanged(w: Float, h: Float){
        backRect.set(0f,0f,w,h)
        val size = w.coerceAtMost(h) * 0.5f
        radiusArray[0] = size.coerceAtMost(radiusArray[0])
        radiusArray[1] = size.coerceAtMost(radiusArray[1])
        radiusArray[2] = size.coerceAtMost(radiusArray[2])
        radiusArray[3] = size.coerceAtMost(radiusArray[3])
        radiusArray[4] = size.coerceAtMost(radiusArray[4])
        radiusArray[5] = size.coerceAtMost(radiusArray[5])
        radiusArray[6] = size.coerceAtMost(radiusArray[6])
        radiusArray[7] = size.coerceAtMost(radiusArray[7])
        resetPath()
        enableWave()
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?: return
        canvas.drawPath(backgroundPath,backPaint)// 绘制背景
        drawWave(canvas)// 绘制水波纹
        if(strokeSize <= 0f) return
        canvas.drawPath(strokePath,strokePaint)// 绘制边框
    }


    /**
     * 启用水波纹
     */
    private fun enableWave(){
        if(waveAnimator.isRunning)waveAnimator.cancel()
        waveWidth = if(waveDirection == Horizontal)backRect.width() else backRect.height()
        waveHeight = waveWidth * amplitude
        if(waveAnimator.isRunning)waveAnimator.cancel()
        waveAnimator.setFloatValues(2 * waveWidth,0f)// 这里交换两个值可以改变波浪行进方向 这里的值必须是两倍于波纹宽度 不然动画重复时可能
        if(waveEnable){ waveAnimator.start() }
    }


    /**
     * 控件尺寸变动、边框尺寸变动、边框圆角变动时需要调用该方法重置绘制路径
     */
    private fun resetPath(){
        backgroundPath.reset()
        backgroundPath.addRoundRect(backRect, radiusArray,Path.Direction.CW)
        val size = strokeSize/2
        strokePath.reset()
        strokePath.addRoundRect(RectF(backRect.left + size,backRect.top + size,backRect.right - size,backRect.bottom - size), radiusArray,Path.Direction.CW)
        clip.reset()
        clip.addRoundRect(RectF(backRect.left + strokeSize,backRect.top + strokeSize,backRect.right - strokeSize,backRect.bottom - strokeSize), radiusArray,Path.Direction.CW)
    }

    /**
     * 绘制水波纹
     */
    private fun drawWave(canvas: Canvas){
        if(!waveEnable){
            // 如果不启用水波纹进度
            wavePath.reset()

            if(waveDirection == Horizontal){
                // 水平方向进度
                val startX = backRect.left + backRect.width() * (progress / maxProgress)
                wavePath.moveTo(startX,backRect.top)
                wavePath.lineTo(startX,backRect.bottom)
                wavePath.lineTo(0f,backRect.bottom)
                wavePath.lineTo(0f,0f)
                wavePath.close()
            }else{
                // 垂直方向进度
                val startY = backRect.bottom - backRect.height() * (progress/maxProgress)
                wavePath.moveTo(0f,startY)
                wavePath.lineTo(backRect.right,startY)
                wavePath.lineTo(backRect.right,backRect.bottom)
                wavePath.lineTo(0f,backRect.bottom)
                wavePath.close()
            }
            canvas.drawPath(clipPath,wavePaint)
            return
        }

        if(waveDirection == Horizontal){
            val startX = (backRect.width() + 2 * waveHeight) * (progress / maxProgress) - waveHeight  // 当前进度绘制起始x坐标

            for(i in 0 until 2){
                wavePath.reset()
                // 起始点x坐标必须保证超出控件右侧 这样才能保证在可视范围内的波浪完整填充控件
                // 这里offset的值为 0 到 2 * waveWidth之间  所以offset - 2 * waveWidth始终为负数，这样就保证x坐标始终超出控件可视范围
                val startY = if(i == 0)  offset - 2 * waveWidth else  offset - 2 * waveWidth  - 0.4f * waveWidth
                var endY = startY
                // 移动画笔到起始坐标
                wavePath.moveTo(startX,startY)

                while (endY < backRect.bottom + waveWidth){// 当下一个起始点坐标在可视范围内时循环画波浪
                    wavePath.rQuadTo(waveHeight,waveWidth/2f,0f,waveWidth)//  画波谷 这里用的相对前一个点的位置 其控制点x为半个波的宽度y为波纹高度 结束点x为一个波的宽度y为0
                    wavePath.rQuadTo(-waveHeight,waveWidth/2f,0f,waveWidth)// 画波峰 同上 但这里画的是波峰 相对屏幕坐标来说是递减的 所以这里y坐标为负的波纹高度
                    endY += 2 * waveWidth // 这里画了一个波峰和一个波谷所以下一个起始点坐标需要加上两个波宽度
                }

                wavePath.lineTo(0f,endY)//连接左下点
                wavePath.lineTo(0f,startY)//连接左上点
                wavePath.lineTo(startX,startY)
                canvas.drawPath(clipPath,wavePaint)
            }
        }else{
            // 这里需要在进度达到最大或最小时波浪效果不可见 所以我们的绘制坐标必须是 - waveHeight 到 waveRect.height() + waveHeight  这里波浪方向是从上向下绘制
            val startY = backRect.height() + waveHeight - (backRect.height() + 2 * waveHeight) * (progress / maxProgress) // 当前进度绘制起始y坐标

            for(i in 0 until 2){
                wavePath.reset()
                // 起始点x坐标必须保证超出控件右侧 这样才能保证在可视范围内的波浪完整填充控件
                // 这里offset的值为 0 到 2 * waveWidth之间  所以offset - 2 * waveWidth始终为负数，这样就保证x坐标始终超出控件可视范围
                val startX = if(i == 0)  offset - 2 * waveWidth else  offset - 2 * waveWidth  - 0.4f * waveWidth
                var endX = startX
                // 移动画笔到起始坐标
                wavePath.moveTo(startX,startY)

                while (endX < backRect.right + waveWidth){// 当下一个起始点坐标在可视范围内时循环画波浪
                    wavePath.rQuadTo(waveWidth/2f,waveHeight,waveWidth,0f)//  画波谷 这里用的相对前一个点的位置 其控制点x为半个波的宽度y为波纹高度 结束点x为一个波的宽度y为0
                    wavePath.rQuadTo(waveWidth/2f,-waveHeight,waveWidth,0f)// 画波峰 同上 但这里画的是波峰 相对屏幕坐标来说是递减的 所以这里y坐标为负的波纹高度
                    endX += 2 * waveWidth // 这里画了一个波峰和一个波谷所以下一个起始点坐标需要加上两个波宽度
                }

                wavePath.lineTo(endX,backRect.bottom)//连接右下点
                wavePath.lineTo(startX,backRect.bottom)//连接左下点
                wavePath.lineTo(startX,startY)
                canvas.drawPath(clipPath,wavePaint)
            }
        }
    }

    /**
     * 设置圆角
     */
    fun setRound(array: FloatArray){
        if(array.size != 8) throw Exception("array.size != 8")
        for(index in array.indices){
            radiusArray[index] = array[index]
        }
        if(viewSize.x != 0f && viewSize.y != 0f ){
            sizeChanged(viewSize.x,viewSize.y)
            postInvalidate()
        }
    }

    /**
     * 设置圆角
     */
    fun setRound(topLeftX: Float = 0f,topLeftY: Float= 0f,topRightX:Float= 0f,topRightY:Float= 0f,bottomLeftX:Float= 0f,bottomLeftY:Float= 0f,bottomRightX:Float= 0f,bottomRightY:Float= 0f){
        radiusArray[0] = topLeftX
        radiusArray[1] = topLeftY
        radiusArray[2] = topRightX
        radiusArray[3] = topRightY
        radiusArray[4] = bottomRightX
        radiusArray[5] = bottomRightY
        radiusArray[6] = bottomLeftX
        radiusArray[7] = bottomLeftY
        if(viewSize.x != 0f && viewSize.y != 0f ){
            sizeChanged(viewSize.x,viewSize.y)
            postInvalidate()
        }
    }
    /**
     * 设置圆角
     */
    fun setRoundRadius(radius: Float){
        radiusArray[0] = radius
        radiusArray[1] = radius
        radiusArray[2] = radius
        radiusArray[3] = radius
        radiusArray[4] = radius
        radiusArray[5] = radius
        radiusArray[6] = radius
        radiusArray[7] = radius
        if(viewSize.x != 0f && viewSize.y != 0f ){
            sizeChanged(viewSize.x,viewSize.y)
            postInvalidate()
        }
    }

    /**
     * 设置边框线宽度
     */
    fun setStrokeSize(size: Float){
        strokeSize = size
        strokePaint.strokeWidth = size
        if(viewSize.x != 0f && viewSize.y != 0f ){
            sizeChanged(viewSize.x,viewSize.y)
            postInvalidate()
        }
    }

    /**
     * 设置背景颜色
     */
    fun backgroundColor(color: Int){
        backColor = color
        backPaint.color = color
        backPaint.shader = null
        postInvalidate()
    }

    /**
     * 背景渐变
     */
    fun backgroundShader(shader: Shader){
        backPaint.shader = shader
        postInvalidate()
    }

    /**
     * 设置水波纹颜色
     */
    fun waveColor(color: Int){
        waveColor = color
        wavePaint.color = color
        wavePaint.shader = null
        postInvalidate()
    }

    /**
     * 渐变水波效果
     */
    fun waveShader(shader: Shader){
        wavePaint.shader = shader
        postInvalidate()
    }

    /**
     * 设置水波纹颜色
     */
    fun strokeColor(color: Int){
        strokeColor = color
        strokePaint.color = color
        strokePaint.shader = null
        postInvalidate()
    }

    /**
     * 渐变水波效果
     */
    fun strokeShader(shader: Shader){
        strokePaint.shader = shader
        postInvalidate()
    }
}